package com.itheima.blog.search.service.impl;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.blog.search.domain.doc.TBlogDOC;
import com.itheima.blog.search.domain.dto.SearchDTO;
import com.itheima.blog.search.domain.po.TBlog;
import com.itheima.blog.search.domain.pojo.PageResult;
import com.itheima.blog.search.mapper.TBlogMapper;
import com.itheima.blog.search.service.ITBlogService;
import com.itheima.common.domain.DTO.LogStateDTO;
import com.itheima.common.domain.R;
import lombok.RequiredArgsConstructor;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 博客文章表 服务实现类
 * </p>
 *
 * @author lin
 * @since 2023-07-12
 */
@Service
@RequiredArgsConstructor
public class TBlogServiceImpl extends ServiceImpl<TBlogMapper, TBlog> implements ITBlogService {

    private final RestHighLevelClient restHighLevelClient;
    private final RestHighLevelClient client;

    @Override
    public R search(SearchDTO search) throws IOException {
//        try {
            //初始化
            SearchRequest request = new SearchRequest("blog");

            // 1.准备Boolean查询
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
            String key = search.getKey();
            boolQuery.must(QueryBuilders.matchQuery("all", key));
            // 2.算分函数查询
            FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery(
                    boolQuery, // 原始查询，boolQuery
                    new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{ // function数组
                            new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                    QueryBuilders.termQuery("isAD", true), // 过滤条件
                                    ScoreFunctionBuilders.weightFactorFunction(10) // 算分函数
                            )
                    }
            );
            // 3.设置查询条件
            request.source().query(functionScoreQuery);


            // 分页
            int page = search.getPage();
            int size = search.getSize();
            request.source().from((page - 1) * size).size(size);

            //高亮
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("title").preTags("<font style = 'color:red'>").postTags("</font>").requireFieldMatch(false);
            request.source().highlighter(highlightBuilder);

            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);

            return R.ok(handleResponse(response));

//        } catch (Exception e) {
//            throw new RuntimeException("搜索数据失败", e);
//        }
    }

    @Override
    public boolean updateStatus(LogStateDTO logStateDTO) {
        return logStateDTO.getStatus() ? displayArticle(logStateDTO.getBlogId()):takenDownArticle(logStateDTO.getBlogId());
    }

    //上架文章
    private boolean displayArticle(Long id)  {
        try{
            TBlog blog = lambdaQuery()
                    .eq(TBlog::getId, id)
                    .one();
            BulkRequest request = new BulkRequest();
            request.add(new IndexRequest("blog").id(blog.getId() + "").source(JSON.toJSONString(blog), XContentType.JSON));
            client.bulk(request, RequestOptions.DEFAULT);

            return true;
        }catch (Exception e) {
            return false;
        }
    }

    //文章下架
    private boolean takenDownArticle(Long id)  {
        try{
            // 1.准备Request
            DeleteRequest request = new DeleteRequest("blog", id.toString());
            // 2.发送请求
            DeleteResponse delete = client.delete(request, RequestOptions.DEFAULT);

            return true;
        }catch (Exception e) {
            return false;
        }
    }




    /**
     * 高亮替换、返回分页
     * @param response
     * @return
     */
    private PageResult handleResponse(SearchResponse response) {
        SearchHits searchHits = response.getHits();
        // 4.1.总条数
        long total = searchHits.getTotalHits().value;
        // 4.2.获取文档数组
        SearchHit[] hits = searchHits.getHits();
        // 4.3.遍历
        List<TBlogDOC> hotels = new ArrayList<>(hits.length);
        for (SearchHit hit : hits) {
            // 4.4.获取source
            String json = hit.getSourceAsString();
            // 4.5.反序列化，非高亮的
            TBlogDOC hotelDoc = JSON.parseObject(json, TBlogDOC.class);
            // 4.6.处理高亮结果
            // 1)获取高亮map
            // 获取高亮结果
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (!CollectionUtils.isEmpty(highlightFields)) {
                // 根据字段名获取高亮结果
                HighlightField highlightField = highlightFields.get("name");
                if (highlightField != null) {
                    // 获取高亮值
                    String name = highlightField.getFragments()[0].string();
                    // 覆盖非高亮结果
                    hotelDoc.setTitle(name);
                }
            }
            // 4.8.排序信息

            // 4.9.放入集合
            hotels.add(hotelDoc);
        }
        return new PageResult(total, hotels);
    }
}
